package com.nulldev.util.internal.backport.httpclient_rw.impl.common;

import static com.nulldev.util.internal.backport.concurrency9.Objects.requireNonNull;

import java.util.concurrent.atomic.AtomicLong;

import com.nulldev.util.internal.backport.concurrency9.concurrent.CompletableFuture;
import com.nulldev.util.internal.backport.concurrency9.concurrent.CompletionStage;

/*
 * A CompletableFuture which does not allow any obtrusion logic.
 * All methods of CompletionStage return instances of this class.
 */
public final class MinimalFuture<T> extends CompletableFuture<T> {

	@FunctionalInterface
	public interface ExceptionalSupplier<U> {
		U get() throws Throwable;
	}

	private final static AtomicLong TOKENS = new AtomicLong();
	private final long id;

	public static <U> MinimalFuture<U> completedFuture(U value) {
		MinimalFuture<U> f = new MinimalFuture<>();
		f.complete(value);
		return f;
	}

	public static <U> CompletableFuture<U> failedFuture(Throwable ex) {
		requireNonNull(ex);
		MinimalFuture<U> f = new MinimalFuture<>();
		f.completeExceptionally(ex);
		return f;
	}

	public static <U> CompletableFuture<U> supply(ExceptionalSupplier<U> supplier) {
		CompletableFuture<U> cf = new MinimalFuture<>();
		try {
			U value = supplier.get();
			cf.complete(value);
		} catch (Throwable t) {
			cf.completeExceptionally(t);
		}
		return cf;
	}

	public MinimalFuture() {
		super();
		this.id = TOKENS.incrementAndGet();
	}

	@Override
	public <U> MinimalFuture<U> newIncompleteFuture() {
		return new MinimalFuture<>();
	}

	@Override
	public void obtrudeValue(T value) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void obtrudeException(Throwable ex) {
		throw new UnsupportedOperationException();
	}

	@Override
	public String toString() {
		return super.toString() + " (id=" + id + ")";
	}

	public static <U> MinimalFuture<U> of(CompletionStage<U> stage) {
		MinimalFuture<U> cf = new MinimalFuture<>();
		stage.whenComplete((r, t) -> complete(cf, r, t));
		return cf;
	}

	private static <U> void complete(CompletableFuture<U> cf, U result, Throwable t) {
		if (t == null) {
			cf.complete(result);
		} else {
			cf.completeExceptionally(t);
		}
	}
}